package org.dru.clay.respository.ivy;

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dru.clay.respository.Repository;
import org.dru.clay.respository.ResolveResult;
import org.dru.clay.respository.artifact.Artifact;
import org.dru.clay.respository.artifact.Group;
import org.dru.clay.respository.artifact.Module;
import org.dru.clay.respository.artifact.UnresolvedArtifact;
import org.dru.clay.respository.artifact.Version;
import org.dru.clay.respository.transport.FileInfo;
import org.dru.clay.respository.transport.Transport;

/**
 * 
 * <ul>
 * <li>[organisation]</li>
 * <li>[module]</li>
 * <li>[revision]</li>
 * <li>[ext]</li>
 * <li>[artifact]</li>
 * </ul>
 * 
 * @param ivyPattern
 *            /[organisation]/[module]/[module]-[revision]/ivy-[revision].xml
 * @param artifactPattern
 *            /[organisation]/[module]/[module]-[revision]/[artifact].[ext]
 * @author erikb
 */
public class IvyRepository implements Repository {
	private static final String P_ORG = "\\[organisation\\]";
	private static final String P_MODULE = "\\[module\\]";
	private static final String P_VERSION = "\\[revision\\]";
	private static final String P_EXT = "\\[ext\\]";
	private static final String P_ARTIFACT = "\\[artifact\\]";

	private static final Pattern VERSION_PATTERN = Pattern.compile(".+-([^-]+)", Pattern.DOTALL);

	private final URI base;
	private final String ivyPattern;
	private final String searchPattern;
	private final String artifactPattern;

	public IvyRepository(URI base, String ivyPattern, String artifactPattern) {
		this.base = base;
		this.ivyPattern = ivyPattern;
		this.artifactPattern = artifactPattern;

		String pattern = "/";
		final StringTokenizer tokenizer = new StringTokenizer(ivyPattern, "/");
		while (tokenizer.hasMoreTokens()) {
			final String token = tokenizer.nextToken();
			if (token.indexOf("[revision]") >= 0) {
				break;
			}
			pattern += token + "/";
		}
		this.searchPattern = pattern;
	}

	@Override
	public URI getLocation() {
		return base;
	}

	@Override
	public Artifact lookup(Transport transport, Group group, UnresolvedArtifact artifact) {
		if (!artifact.getVersionPattern().isDynamic()) {
			// The version isn't dynamic so it can be 'looked-up' directly
			final Version version = Version.fromString(artifact.getVersionPattern().getOriginal());
			return new Artifact(artifact.getName(), version, artifact.getExtension(), artifact.getClassifier());
		}

		try {
			Version latest = null;

			final URI directory = calculateSearchURI(group, artifact);
			for (URI uri : transport.list(directory)) {
				final String moduleVersion = lastPathElement(uri.getPath());
				final Matcher matcher = VERSION_PATTERN.matcher(moduleVersion);
				if (!matcher.matches()) {
					// invalid module link
					continue;
				}

				final Version version = Version.fromString(matcher.group(1));
				if (version.compareTo(latest) > 0) {
					latest = version;
				}
			}

			if (latest == null) {
				throw new IllegalArgumentException("Unable to find latest version");
			}

			return new Artifact(artifact.getName(), latest, artifact.getExtension(), artifact.getClassifier());

		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public ResolveResult resolve(Transport transport, Module module) {
		try {
			final URI ivyUri = calculateIvyURI(module);
			final FileInfo fileInfo = transport.get(ivyUri);
			final IvyXml ivyXml = new IvyXmlParser().parse(module, fileInfo.getContent());
			return ivyXml;
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void get(Transport transport, Module module, Artifact artifact, File destination) {
		try {
			final URI uri = calculateArtifactURI(module, artifact);
			transport.get(uri, destination);
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	public void put(Transport transport, Module module, Artifact artifact, File source) {
		throw new UnsupportedOperationException("Not implemented");
	}

	protected URI calculateIvyURI(Module module) throws URISyntaxException {
		final Group group = module.getGroup();
		final Artifact artifact = module.getArtifact();
		final String path = ivyPattern.replaceAll(P_ORG, group.get()).replaceAll(P_MODULE, artifact.getName())
				.replaceAll(P_VERSION, artifact.getVersion().plain());
		return new URI(base + path);
	}

	protected URI calculateSearchURI(Group group, UnresolvedArtifact artifact) throws URISyntaxException {
		final String path = searchPattern.replaceAll(P_ORG, group.get()).replaceAll(P_MODULE, artifact.getName());
		return new URI(base + path);
	}

	protected URI calculateArtifactURI(Module module, Artifact artifact) throws URISyntaxException {
		final Group group = module.getGroup();
		final String path = artifactPattern.replaceAll(P_ORG, group.get()).replaceAll(P_MODULE, module.getArtifact().getName())
				.replaceAll(P_VERSION, module.getArtifact().getVersion().plain()).replaceAll(P_ARTIFACT, artifact.asString())
				.replaceAll(P_EXT, artifact.getExtension());
		return new URI(base + path);
	}

	protected String lastPathElement(String path) {
		if (path.endsWith("/")) {
			path = path.substring(0, path.length() - 1);
		}
		return path.substring(path.lastIndexOf('/') + 1);
	}
}
